package pl.druminski.robinhood.controller;

import java.awt.TrayIcon.MessageType;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import pl.druminski.robinhood.view.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Calendar;
import javax.swing.JOptionPane;
import pl.druminski.robinhood.model.Data;
import pl.druminski.robinhood.model.PluginInfo;
import pl.druminski.robinhood.model.UpdatingInfo;
import pl.druminski.robinhood.plugininterface.Plugin;

/**
 * @author Łukasz Drumiński
 * 
 */
public class Engine implements Runnable
{

    private Tray tray;
    private Data data;
    private Plugin[] plugins;
    private String info;

    public Engine(Tray tray, Data data)
    {
        this.data = data;
        this.tray = tray;
        //przypisz akcje ikonie tray
        tray.trayIcon.addActionListener(new ALTray(data));

        //przypisz akcje elementom z menu, ktore jest podpiete pod ikone w tray
        tray.itemExit.addActionListener(new ALExit());
        tray.itemOptions.addActionListener(new ALOptions(data));
        tray.itemRun.addActionListener(new ALRun(this));

    }

    public void run()
    {

        waitForInternetConnection();

        //ładowanie pluginów

        //znajdz pluginy, czyli pliki jar
        URL[] jarFiles = findAllJars();

        if (jarFiles != null)
        {
            //zaladuj pliki jar przy pomocy URLClassLoader
            URLClassLoader cl = new URLClassLoader(jarFiles);

            //stworz tablice pluginow
            plugins = new Plugin[jarFiles.length];
            String pluginName;

            //zaladuj klasy z plikow jar i utworz obiekty-pluginy
            for (int i = 0; i < jarFiles.length; i++)
            {
                pluginName = getPluginName(jarFiles[i]);
                try
                {
                    loadPlugin(cl, pluginName, i);

                } catch (Exception ex)
                {
                    Logger.getLogger(data.getLogName()).warning("Problem przy ladowaniu pluginu " + pluginName + ". " + ex);
                }

            }

            //sprawdz czy sa dostepne aktualizacje
            checkUpdate();

            //uzupelnienie informacji o dostepnych pluginach
            verifyPlugins();

            sleepBeforRunPlugins();

            //rozruch pluginów
            runPlugins();

            sleepAndExitAfterRunPlugins();
        }

    }

    private URL[] findAllJars()
    {
        String dirname = data.getDirPluginName();
        File f1 = new File(dirname);
        FilenameFilter jars = new JarFilter();
        String[] s = f1.list(jars);
        URL[] url;
        url = new URL[s.length];
        File file;
        URI uri;

        if (s != null)
        {
            for (int i = 0; i < s.length; i++)
            {
                try
                {
                    file = new File(dirname + data.getSeparator() + s[i]);
                    uri = file.toURI();
                    url[i] = uri.toURL();
                } catch (MalformedURLException ex)
                {
                    Logger.getLogger(data.getLogName()).warning("Wyjątek przy wybieraniu pluginów. Problem z: " + s[i] + "," + ex);
                }
            }
        }

        return url;
    }

    private void loadPlugin(URLClassLoader cl, String pluginName, int number) throws
            InstantiationException, IllegalAccessException, ClassNotFoundException
    {
        Plugin plugin;
        String className = data.getPackagePluginName() + "." + pluginName;
        plugin = (Plugin) Class.forName(className, true, cl).newInstance();

        plugins[number] = plugin;

    }

    /**
     * Sprawdza czy sa dostepne aktualizacje, jeśli tak to podejmuje konkretne dzialania
     */
    public void checkUpdate()
    {
        //aktualizacja
        if (data.getUpdatingMethod() != UpdatingInfo.NONE)
        {
            //sprawdz czy sa dostepne aktualizacje
            Updating updating = new Updating(data);

            Boolean update = updating.CheckForUpdates(plugins);

            if (update)
            {
                int result = 0;

                if (data.getUpdatingMethod() == UpdatingInfo.MANUAL)
                {
                    //uzyskaj zgode od uzytkownika na aktualizacje
                    result = JOptionPane.showConfirmDialog(
                            null,
                            "Czy chcesz dokonać aktualizacji?",
                            "Dostępna jest aktualizacja programu RobinHood.",
                            JOptionPane.YES_NO_OPTION);
                }

                if (result == 0)
                {
                    System.out.println("Potrzbena aktualizacja");
                    try
                    {
                        //wykonaj aktualizacje

                        File file = new File("Updater.Jar");
                        if (file.exists())
                        {
                            Runtime.getRuntime().exec("java -jar Updater.jar");
                        }
                        Logger.getLogger(data.getLogName()).info("Zakonczenie pracy aplikacji (w celu aktualizacji)");
                        System.exit(0);
                    } catch (IOException ex)
                    {
                        Logger.getLogger(data.getLogName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

        }

    }

    private void sleepBeforRunPlugins()
    {
        tray.setRun(true);

        if (data.getStartTime() > 0)
        {
            tray.trayIcon.setToolTip("Odliczam do przeprowadzenia symulacji kliknięć");
            //poczekaj pare minut zgodnie z zyczeniem uzytkownika
            try
            {
                Thread.sleep(1000 * 60 * data.getStartTime());

            } catch (InterruptedException ex)
            {
            }
        }

    }

    private void sleepAndExitAfterRunPlugins()
    {
        tray.trayIcon.setToolTip(info + "\nOdliczam do zamknięcia aplikacji.");

        //wylacz program jeśli jest ustawiony ExitTime
        if (data.getExitTime() >= 0)
        {
            //wylacz program zgodnie z zyczeniem uzytkownika
            if (data.getExitTime() > 0)
            {
                //poczekaj pare minut zgodnie z zyczeniem uzytkownika
                try
                {
                    Thread.sleep(1000 * 60 * data.getExitTime());

                } catch (InterruptedException ex)
                {
                }
            }

            //wylacz program
            Logger.getLogger("RobinHoodLog").info("Zamknięcie aplikacji");
            System.exit(0);
        }
    }

    public void verifyPlugins()
    {
        Boolean change = false;

        for (int i = 0; i < plugins.length; i++)
        {
            Plugin plugin = plugins[i];
            Boolean exist = false;
            for (int j = 0; j < data.pluginInfo.size(); j++)
            {
                PluginInfo pluginInfo = data.pluginInfo.get(j);
                if (plugin.getName().equals(pluginInfo.getName()))
                {
                    exist = true;
                    pluginInfo.setValueOfOneClickInPennies(plugin.getValueOfOneClickInPennies());
                }
            }

            if (!exist)
            {
                //nie ma pluginu na liscie informacji o pluginach, dodaj go
                PluginInfo newInfo = new PluginInfo(plugin.getName(), true, 0);
                Calendar cal = Calendar.getInstance();
                int day = cal.get(Calendar.DAY_OF_MONTH);
                int month = cal.get(Calendar.MONTH) + 1;
                int year = cal.get(Calendar.YEAR);

                newInfo.setAge(day, month, year);
                newInfo.setValueOfOneClickInPennies(plugin.getValueOfOneClickInPennies());
                data.pluginInfo.add(newInfo);
                change = true;
            }
        }

        if (change)
        {
            data.saveConfig();
        }
    }

    private String getPluginName(URL url)
    {
        String name = url.getPath();

        name = name.substring(name.lastIndexOf('/') + 1, name.length() - ".jar".length());

        return name;
    }

    private void waitForInternetConnection()
    {

        boolean status = false;
        tray.setRun(false);

        int sleep = data.getSleep();
        InternetChecker checker = new InternetChecker();

        tray.trayIcon.setToolTip("Czekam na połączenie internetowe.");
        //czekaj na polaczenie z Internetem
        while (status == false)
        {
            status = checker.checkConnection();
            if (!status)
            {
                try
                {
                    Thread.sleep(sleep);

                } catch (InterruptedException ex)
                {
                }
            }
        }
    }

    public void runPlugins()
    {
        tray.setRun(false);
        tray.trayIcon.setToolTip("Trwa symulowanie kliknięć.");
        int size = data.proxyList.size();
        //utworz tablice proxy
        Proxy[] tabProxy = new Proxy[size];

        for (int i = 0; i < size; i++)
        {
            tabProxy[i] = (Proxy) data.proxyList.get(i);
        }

        //uruchamianie pluginow
        int[] clicks = new int[plugins.length];
        int tmp;
        int sumClicks = 0;
        int sumMoney = 0;
        for (int i = 0; i < plugins.length; i++)
        {
            if (data.isLoadablePlugin(plugins[i].getName()))
            {
                tmp = plugins[i].Run(tabProxy);
                clicks[i] = tmp;
                if (tmp > 0)
                {
                    sumClicks += tmp;
                    sumMoney += tmp * plugins[i].getValueOfOneClickInPennies();
                    data.addClicks(plugins[i].getName(), tmp);
                }
            }

        }

        if (sumClicks > 0)
        {
            //zapisz klikniecia
            data.saveConfig();
        }


        if (sumMoney >= 100)
        {
            double m = sumMoney;
            m = m / 100;
            info = "Przekazałeś potrzebującym " + m + "zł !";
        } else
        {
            info = "Przekazałeś potrzebującym " + sumMoney + " groszy!";
        }

        tray.trayIcon.displayMessage("Robin Hood", info, MessageType.INFO);
        tray.trayIcon.setToolTip(info);
        tray.setRun(true);
    }
}
